#ifndef _AMERICO_UTILITIES_DEBUG_H_
#define _AMERICO_UTILITIES_DEBUG_H_

#include <bitset>
#include <cstdarg>
#include "os_file.h"
#ifdef _DEBUG
#include "Windows.h"
#endif // _DEBUG

// Statements like:
//		#pragma message(Reminder "Fix this problem!")
// Which will cause messages like:
//		C:\Source\Project\main.cpp(47): Reminder: Fix this problem!
// to show up during compiles.  Note that you can NOT use the
// words "error" or "warning" in your reminders, since it will
// make the IDE think it should abort execution.  You can double
// click on these messages and jump to the line in question.
#define Stringize( L )			#L
#define MakeString( M, L )		M(L)
#define $Line					\
	MakeString( Stringize, __LINE__ )
#define Reminder				\
	__FILE__ "(" $Line ") : warning Reminder: "
#define Todo				\
	__FILE__ "(" $Line ") : warning TODO: "
#define Note				\
	__FILE__ "(" $Line ") : message Note: "

#ifndef _PRINT_DEBUG_OUTPUT
#define _PRINT_DEBUG_OUTPUT 0
#endif

//#pragma message (Reminder "Do not comment out DebugMessage! That's why '_PRINT_DEBUG_OUTPUT' exists!")
#ifdef _DEBUG
 #if _PRINT_DEBUG_OUTPUT
 #define DebugMessage(str)	OutputDebugStringA(str)
 #else
#define DebugMessage(str)
#endif
#else
#define DebugMessage(str)
#endif // _DEBUG

namespace Utilities
{
	namespace Debug
	{
		enum DebugLevel
		{
			DebugAll      = 0,
			DebugWarning  = 1,
			DebugError    = 2,
			DebugSevere   = 3,
			DebugCritical = 4,
			DebugNone
		};

		void Assert(const std::string aFile, const int &aLine, const std::string aFunction, const std::string aMessage);
		void Log(const DebugLevel &aDebugLevel, const std::string aMessage);
		void Log(const DebugLevel &aDebugLevel, const std::string aFile, const int &aLine, const std::string aFunction, const std::string aMessage);
		void Log(const DebugLevel &aDebugLevel, const std::string aMessage, const double &aDouble);
		void Log(const DebugLevel &aDebugLevel, const std::string aFile, const int &aLine, const std::string aFunction, const std::string aMessage, const double &aDouble);

		void SetDebugLevel(const DebugLevel &aDebugLevel);

		class Debug
		{
		public:
			static std::string DebugLogFilename;

			static Debug *GetInstance(const OS::FileFlags &someFlags=OS::APPEND);

			static void Assert(const DebugLevel &aDebugLevel, const bool &aCondition, const std::string aMessage);
			static void Assert(const DebugLevel &aDebugLevel, const bool &aCondition, const std::string aFile, const int &aLine, const std::string aFunction, const std::string aMessage);

			static void Log(const DebugLevel &aDebugLevel, const std::string aMessage);
			static void Log(const DebugLevel &aDebugLevel, const std::string aFile, const int &aLine, const std::string aFunction, const std::string aMessage);

			static void SetDebugLevel(const DebugLevel &aDebugLevel);
			void IncreaseTabCount();
			void DecreaseTabCount();

		private:
			static Debug *ourInstance;

			OS::File myErrLog;
			DebugLevel myDebugLevel;
			unsigned int myTabCount;

			Debug(const OS::FileFlags &someFlags=OS::APPEND);
			~Debug();

			void Print(std::string errorMessage);
			void Warning(std::string errorMessage);
			void Error(std::string errorMessage);
			void Severe(std::string errorMessage);
			void Critical(std::string errorMessage);

			void WriteString(std::string aMessage);
		};
	};
};
namespace Utils = Utilities;

#endif
